package com.jaws.core.configuration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.DispatcherType;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.google.common.base.Charsets;
import com.jaws.core.endpoint.FeignClientEndpoint;
import com.jaws.core.mvc.aop.ReqRspAspect;
import com.jaws.core.mvc.exception.CommonHandlerExceptionResolver;
import com.jaws.core.mvc.feign.JawsFeignClient;
import com.jaws.core.mvc.filter.CompositeFilter;
import com.jaws.core.mvc.filter.LogMDCFilter;
import com.jaws.core.mvc.interceptor.JawsFeignInterceptor;
import com.jaws.core.mvc.view.JawsExcelView;
import com.jaws.core.mvc.view.JawsViewResolver;

import feign.Client;
import feign.codec.Decoder;

/**
 * 仅在mvc时候生效
 *
 * @author maojian
 * @date 2018-03-01 17:39:05
 * @since v1.0.0
 */

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(MvcProperties.class)
// @ConditionalOnProperty(prefix = "yjs.jaws", name = "application.mvc", havingValue = "true", matchIfMissing = true)
public class MvcConfiguration extends WebMvcConfigurerAdapter {

	@Resource
	private MvcProperties mvcProperties;

	/**
	 * 加入异常链
	 *
	 * @param exceptionResolvers
	 * @author maojian
	 * @date 2018-03-01 17:40:32
	 * @since v1.0.0
	 */
	@Override
	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		exceptionResolvers.add(commonHandlerExceptionResolver());
	}

	/**
	 * 异常
	 *
	 * @return
	 * @author maojian
	 * @date 2018-03-01 17:39:44
	 * @since v1.0.0
	 */
	@Bean
	public CommonHandlerExceptionResolver commonHandlerExceptionResolver() {
		CommonHandlerExceptionResolver resolver = new CommonHandlerExceptionResolver();
		resolver.setJawsExceptions(mvcProperties.getExceptions());
		return resolver;
	}

	/**
	 * controller拦截
	 *
	 * @return
	 * @author maojian
	 * @date 2018-03-01 17:39:53
	 * @since v1.0.0
	 */
	@Bean
	public ReqRspAspect reqRspAspect() {
		return new ReqRspAspect();
	}

	/**
	 * filter过滤链
	 *
	 * @return
	 * @author maojian
	 * @date 2018-03-01 17:40:03
	 * @since v1.0.0
	 */
	@Bean
	public CompositeFilter compositeFilter() {
		CompositeFilter compositeFilter = new CompositeFilter();
		compositeFilter.setFilters(Arrays.asList(new CharacterEncodingFilter(), logMDCFilter()));
		return compositeFilter;
	}

	@Bean
	public LogMDCFilter logMDCFilter() {
		return new LogMDCFilter();
	}

	/**
	 * filter注册
	 *
	 * @return
	 * @author maojian
	 * @date 2018-03-01 18:00:42
	 * @since v1.0.0
	 */
	@Bean
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
		DelegatingFilterProxy filterProxy = new DelegatingFilterProxy("compositeFilter");
		filterProxy.setTargetFilterLifecycle(true);
		filterRegistration.setFilter(filterProxy);
		filterRegistration.setEnabled(true);
		filterRegistration.addUrlPatterns("/*");
		filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
		return filterRegistration;
	}

	@Bean
	public JawsExcelView jawsExcelView() {
		return new JawsExcelView();
	}

	@Bean
	public JawsViewResolver jawsViewResolver() {
		return new JawsViewResolver();
	}

	/**
	 * 视图配置
	 *
	 * @param registry
	 */
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		super.configureViewResolvers(registry);
		registry.viewResolver(jawsViewResolver());
	}

	/**
	 * 自定义转换器
	 *
	 * @param converters
	 * @author maojian
	 * @date 2018-03-08 16:50:51
	 * @since v1.0.0
	 */
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		// xml text
		StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
		List<MediaType> StringMediaTypes = new ArrayList<>();
		StringMediaTypes.add(MediaType.TEXT_PLAIN);
		StringMediaTypes.add(MediaType.TEXT_XML);
		stringConverter.setSupportedMediaTypes(StringMediaTypes);
		converters.add(0, stringConverter);

		// 1. 定义一个convert 转换消息的对象
		FastJsonHttpMessageConverter jsonConverter = new FastJsonHttpMessageConverter();
		// 2.添加fastjson 的配置信息 比如 是否要格式化 返回的json数据
		FastJsonConfig fastJsonConfig = new FastJsonConfig();
		fastJsonConfig.setCharset(Charsets.UTF_8);
		fastJsonConfig.setFeatures(Feature.IgnoreNotMatch);
		fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
		fastJsonConfig.setSerializerFeatures(
		// 格式化
		// SerializerFeature.PrettyFormat,
		// key 值加引号
				SerializerFeature.QuoteFieldNames,
				// 空值处理
				SerializerFeature.WriteNullStringAsEmpty,
				// SerializerFeature.WriteNullNumberAsZero,
				SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty,
				// 处理循环引用
				SerializerFeature.DisableCircularReferenceDetect,
				// 格式化日期
				SerializerFeature.WriteDateUseDateFormat);

		// V1.0.3开始Long 不再转String
		// SerializeConfig serializeConfig = SerializeConfig.globalInstance;
		// serializeConfig.put(Long.class, ToStringSerializer.instance);
		// fastJsonConfig.setSerializeConfig(serializeConfig);

		jsonConverter.setFastJsonConfig(fastJsonConfig);
		// json
		List<MediaType> jsonMediaTypes = new ArrayList<>();
		jsonMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
		jsonConverter.setSupportedMediaTypes(jsonMediaTypes);
		converters.add(1, jsonConverter);

		super.configureMessageConverters(converters);
	}

	// @Bean
	// public ServletRegistrationBean druidServlet() {
	// ServletRegistrationBean reg = new ServletRegistrationBean();
	// reg.setServlet(new StatViewServlet());
	// reg.addUrlMappings("/druid/*");
	// reg.addInitParameter("loginUsername", username);
	// reg.addInitParameter("loginPassword", password);
	// reg.addInitParameter("logSlowSql", logSlowSql);
	// return reg;
	// }

	/**
	 * interceptor
	 *
	 * @param registry ''
	 * @author maojian
	 * @date 2018-03-01 17:40:18
	 * @since v1.0.0
	 */
	// @Override
	// public void addInterceptors(InterceptorRegistry registry) {
	// registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");
	// }

	@Configuration
	@Profile("dev")
	@EnableSwagger2
	protected class JawsSwaggerConfiguration extends WebMvcConfigurerAdapter {

		@Override
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
			registry.addResourceHandler(new String[] { "swagger-ui.html" }).addResourceLocations(new String[] { "classpath:/META-INF/resources/" });
			registry.addResourceHandler(new String[] { "/webjars/**" }).addResourceLocations(new String[] { "classpath:/META-INF/resources/webjars/" });
		}

		@Bean
		public Docket createRestApi(MvcProperties mvcProperties) {
			return (new Docket(DocumentationType.SWAGGER_2)).apiInfo(this.apiInfo(mvcProperties)).select().apis(RequestHandlerSelectors.basePackage(mvcProperties.getSwaggerBasepackage()))
					.paths(PathSelectors.any()).build();
		}

		private ApiInfo apiInfo(MvcProperties mvcProperties) {
			return (new ApiInfoBuilder()).title(mvcProperties.getSwaggerServiceName() + " Restful APIs").description(mvcProperties.getSwaggerServiceDescription())
					.contact(mvcProperties.getSwaggerServiceName()).version(mvcProperties.getSwaggerServiceVersion()).build();
		}
	}

	@Configuration
	@ConditionalOnClass({ FeignClient.class, Decoder.class })
	protected class FeignConfig {

		@Bean
		public JawsFeignInterceptor jawsFeignInterceptor() {
			return new JawsFeignInterceptor();
		}

		@Bean
		@Profile("dev")
		@ConditionalOnProperty(prefix = "yjs.jaws", name = "mock", havingValue = "true", matchIfMissing = false)
		public Client jawsFeignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
			return new JawsFeignClient(new Client.Default(null, null), cachingFactory, clientFactory);
		}

		@Bean
		public FeignClientEndpoint feignClientEndpoint() {
			return new FeignClientEndpoint();
		}

		@Bean
		public Decoder feignDecoder() {
			return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
		}

		public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
			// 定义一个convert 转换消息的对象
			FastJsonHttpMessageConverter jsonConverter = new FastJsonHttpMessageConverter();
			// 2 添加fastjson 的配置信息 比如 是否要格式化 返回的json数据
			FastJsonConfig fastJsonConfig = new FastJsonConfig();
			fastJsonConfig.setCharset(Charsets.UTF_8);
			fastJsonConfig.setFeatures(Feature.IgnoreNotMatch);
			fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

			fastJsonConfig.setSerializerFeatures(
			// 格式化
			// SerializerFeature.PrettyFormat,
			// key 值加引号
					SerializerFeature.QuoteFieldNames,
					// 空值处理
					SerializerFeature.WriteNullStringAsEmpty,
					// SerializerFeature.WriteNullNumberAsZero,
					SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty,
					// 处理循环引用
					SerializerFeature.DisableCircularReferenceDetect,
					// 格式化日期
					SerializerFeature.WriteDateUseDateFormat);

			jsonConverter.setFastJsonConfig(fastJsonConfig);
			// json
			List<MediaType> jsonMediaTypes = new ArrayList<>();
			jsonMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
			jsonConverter.setSupportedMediaTypes(jsonMediaTypes);
			HttpMessageConverters httpMessageConverters = new HttpMessageConverters(jsonConverter);
			return new ObjectFactory<HttpMessageConverters>() {
				@Override
				public HttpMessageConverters getObject() throws BeansException {
					return httpMessageConverters;
				}
			};
		}
	}

}
